﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using dotNails.CodeGen.Models;
using dotNails.CodeGen.Util;
using System.Text.RegularExpressions;

namespace dotNails.CodeGen.Models.dotNailsLinqToSqlGenerator {
	public class TableTemplate : BaseCodeTemplate {
		#region Constants
		// TODO: This list is not complete yet
		List<string> RESERVED_LINQ_WORDS = new List<string>() {
										"from",
										"group",
										"select",
										"in",
										"where",
										"orderby"
									 };

		#endregion Constants
		
		public Table Table { get; set; }
		public string DataContextClass { get; set; }
		public ConfigData ConfigData { get; set; }

		public string GetCSharpVariableName(Column column) {
			return column.GetPropertyName();
		}

		public string GetUpdateCheckAttribute(Column column) {
			string attribute = string.Empty;

			if (!string.IsNullOrEmpty(column.UpdateCheck)) {
				attribute = ", UpdateCheck=UpdateCheck." + column.UpdateCheck;
			}

			return attribute;
		}

		public bool IsExcludedColumn(string columnName) {
			bool result = false;

			// Don't think we can use lambda's here due to the ancient CodeSmith library's limitations
			foreach (ExcludedColumn column in ConfigData.Config.ExcludedColumns) {
				if (column.DBName == columnName) {
					result = true;
					break;
				}
			}

			return result;
		}

		public string GetAutoSyncAttribute(Column column) {
			string attribute = string.Empty;

			//if (!string.IsNullOrEmpty(column.AutoSync)) {
			//    attribute = ", AutoSync=AutoSync." + column.AutoSync;
			//}
			//else 
			if (column.IsDbGenerated) {
				attribute = ", AutoSync=AutoSync.OnInsert";
			}

			return attribute;
		}

		public string GetThisKeyAttribute(Association association) {
			string attribute = string.Empty;

			if (!string.IsNullOrEmpty(association.ThisKey)) {
				attribute = ", ThisKey=\"" + association.ThisKey + "\"";
			}

			return attribute;
		}

		public string GetIsPrimaryKeyAttribute(Column column) {
			string attribute = string.Empty;

			if (column.IsPrimaryKey) {
				attribute = ", IsPrimaryKey=true";
			}

			return attribute;
		}

		public string GetIsDbGeneratedAttribute(Column column) {
			string attribute = string.Empty;

			if (column.IsDbGenerated) {
				attribute = ", IsDbGenerated=true";
			}

			return attribute;
		}

		public string GetIsForeignKeyAttribute(Association association) {
			string attribute = string.Empty;

			if (association.IsForeignKey) {
				attribute = ", IsForeignKey=true";
			}

			return attribute;
		}

		// If there is an assoc with the same ThisKey value as the column name, AND that association is a foreign key
		public bool IsColumnAForeignKey(Column column) {
			bool result = false;
			Association association = Table.Type.Associations.Find(a => a.ThisKey == column.Name);

			if (association != null && association.IsForeignKey) {
				result = true;
			}

			return result;
		}

		public string GetForeignKeyMember(Column column) {
			string foreignKeyMember = string.Empty;
			Association association = Table.Type.Associations.Find(a => a.ThisKey == column.Name);

			if (association != null) {
				foreignKeyMember = association.Member;
			}

			return foreignKeyMember;
		}

		public Column FindColumn(string columnName) {
			Column column = Table.Type.Columns.Find(c => c.GetPropertyName() == columnName);
			return column;
		}

		public string GetCSharpVariableType(Column column) {
			//return base.GetCSharpVariableType(column.Type, column.CanBeNull);
			// Altered to always use nullable types for purposes of determining when properties have been modified
			return base.GetCSharpVariableType(column.Type, true);
		}

		public string GetCSharpPrimaryKeyParmList(Table table) {
			StringBuilder parmList = new StringBuilder();

			// We're trying to build something like this:
			//  int id
			bool isFirstKey = true;
			foreach (Column column in table.Type.Columns) {
				if (column.IsPrimaryKey) {
					if (isFirstKey) {
						isFirstKey = false;
					}
					else {
						parmList.Append(", ");
					}
					string variableType = GetCSharpVariableType(column.Type, false);
					string variableName = StringUtil.GetCamelCaseName(GetCSharpVariableName(column));
					parmList.Append(variableType).Append(" ").Append(variableName);
				}
			}

			return parmList.ToString();
		}

		public string GetCSharpPrimaryKeyLambdaExpression(Table table) {
			StringBuilder lambdaExpression = new StringBuilder();

			// We're trying to build something like this:
			//  i => i.ID == ID
			string tableAbbreviation = GetLowercaseAbbreviation(table.Type.Name);
			lambdaExpression.Append(tableAbbreviation).Append(" => ");

			bool isFirstKey = true;
			foreach (Column column in table.Type.Columns) {
				if (column.IsPrimaryKey) {
					if (isFirstKey) {
						isFirstKey = false;
					}
					else {
						lambdaExpression.Append(" && ");
					}
					string variableName = GetCSharpVariableName(column);
					lambdaExpression.Append(tableAbbreviation).Append(".").Append(variableName).Append(" == ").Append(StringUtil.GetCamelCaseName(variableName));
				}
			}

			return lambdaExpression.ToString();
		}

		public string GetPrimaryKeyNullCheck(Table table) {
			StringBuilder nullCheck = new StringBuilder();

			// We're trying to build something like this:
			//  ID != 0
			bool isFirstKey = true;
			foreach (Column column in table.Type.Columns) {
				if (column.IsPrimaryKey) {
					if (isFirstKey) {
						isFirstKey = false;
					}
					else {
						nullCheck.Append(" && ");
					}
					nullCheck.Append("_" + GetCSharpVariableName(column) + "Dirty");
				}
			}

			return nullCheck.ToString();
		}

		public string GetLinqInstanceName(Table table) {
			string instanceName = table.Type.Name;

			if (IsReservedLinqWord(instanceName)) {
				instanceName = "my" + StringUtil.GetPascalCaseName(instanceName);
			}
			else {
				instanceName = StringUtil.GetCamelCaseName(instanceName);
			}
		
			return instanceName;
		}

		public bool IsReservedLinqWord(string stringToCheck) {
			bool result = false;

			if (IsReservedWord(stringToCheck)) {
				result = true;
			}
			else {
				result = RESERVED_LINQ_WORDS.Contains(stringToCheck.Trim().ToLower());
			}

			return result;
		}

		public string CreateManyToManyPropertyName(Association association) {
			string propertyName = association.ManyToManyJoin.OtherPropertyName;

			// Check to see if any associations use this name as a member (if so, we'll have a conflict because there's already an EntitySet or EntityRef using this name)
			propertyName = CheckForExistingPropertyName(propertyName);

			return propertyName;
		}

		private string CheckForExistingPropertyName(string name) {
			string newName = name;

			Association existingAssociation = Table.Type.Associations.Find(a => a.Member == name);
			if (existingAssociation != null) {
				// See if last character is a number
				Match match = Regex.Match(name.Substring(name.Length - 1), @"\d");

				if (!string.IsNullOrEmpty(match.Value)) {
					// The existing name ends in a number, so increment it by one
					newName = name.Substring(0, name.Length - 1) + (Convert.ToInt32(match.Value) + 1).ToString();
					// There may already be another association with the same name + 1, so check again - keep going
					newName = CheckForExistingPropertyName(newName);
				}
				else {
					newName = name + "1";
				}
			}

			return newName;
		}
		
	}
}
